/*Copyright (c) 2009 Michael Streif
 *
 *Permission is hereby granted, free of charge, to any person obtaining a copy
 *of this software and associated documentation files (the "Software"), to deal
 *in the Software without restriction, including without limitation the rights
 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *copies of the Software, and to permit persons to whom the Software is
 *furnished to do so, subject to the following conditions:
 *
 *The above copyright notice and this permission notice shall be included in
 *all copies or substantial portions of the Software.
 *
 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *THE SOFTWARE.
 */

#include "kmalloc.h"
#include "vmm.h"
#include "console.h"

#define KERNEL_HEAP_START 0xA00000	  // at 10 MB virtual

#define ALIGN_UP(align, num) (align + ((num - 1) & ~(align - 1)))

/*typedef struct
{
	uint64				size;
	struct freeBlock*   next;
	struct freeBlock*   prev;
} freeBlock;


freeBlock* startBlock = (freeBlock*) KERNEL_HEAP_START;

int init = 0;

uint64* kmalloc(uint64 size)
{
	freeBlock* currentBlock = startBlock;
	freeBlock* newBlock;

	//TODO: implement malloc for amounts over 4kb!
	if (size > 4096)
	{
		return 0;
	}

	if (!init)
	{
		startBlock->size = 4096;
		startBlock->next = 0;
		startBlock->prev = 0;

		mapPage((uint64*) (KERNEL_HEAP_START), KERNEL_PAGE);

		init = 1;
	}

	do
	{
		if (size <= (currentBlock->size - sizeof(freeBlock)))
		{
			// if there remains at least one byte after splitting the block,
			// split it!
			if ((currentBlock->size - size) > sizeof(freeBlock))
			{
				newBlock = currentBlock + size;

				newBlock->size = currentBlock->size - size;
				newBlock->prev = currentBlock->prev;
				newBlock->next = currentBlock->next;
			}

			return (uint64*) currentBlock;
		}

		currentBlock = currentBlock->next;

	} while (currentBlock->next);

	// didn't find a suitable free block
	newBlock = (freeBlock*) (currentBlock + currentBlock->size);

	mapPage((uint64*) (newBlock), KERNEL_PAGE);

	// if there remains at least one byte after splitting the block,
	// split it!
	if ((newBlock->size - size) > sizeof(freeBlock))
	{
		freeBlock* lastBlock;

		lastBlock = newBlock + size;

		lastBlock->size = newBlock->size - size;
		lastBlock->prev = newBlock->currentBlock;
		lastBlock->next = 0;
	}
	else
	{
		// newBlock is the last block!
		newBlock->size = size;
		newBlock->next = 0;
		newBlock->prev = currentBlock;
	}

	return (uint64*) newBlock;
}*/

uint8* heapEnd = (uint8*) KERNEL_HEAP_START;
uint8* heapPos = (uint8*) KERNEL_HEAP_START;

void* kmalloc(uint64 size)
{
	uint8* retVal = 0;

	if ((heapPos + size - 1) < heapEnd)
	{
		retVal = heapPos;
		heapPos += size - 1;
	}
	else
	{
		uint64 num = ALIGN_UP(4096, size) / 4096;

		while (num > 0)
		{
			mapPage((uint64*) heapEnd, KERNEL_PAGE);
			heapEnd += 4096;
			num--;
		}

		retVal = heapPos;
		heapPos += size - 1;
	}

	return retVal;
}


void free(void* addr)
{}


